package cn.wangkai.peanut.util.mvc;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import cn.wangkai.peanut.util.RequestContext;
import freemarker.template.Template;
import freemarker.template.TemplateException;

public final class ActionServlet extends HttpServlet {
	
	private static Log log = LogFactory.getLog(ActionServlet.class);
	private static final long serialVersionUID = 1L;
	private String pkg = null;
	private boolean isdebug = false;
	public String Extension = "wk";
	private static HashMap<String, UrlPattern> urlpattern;
	private static Map<String,Template> templates;	
	static{
		log.debug("全局初始化...........");
		urlpattern = new HashMap<String, UrlPattern>();
		templates = new HashMap<String, Template>();
	}
	
	public void init() throws ServletException {
		log.debug("初始化......");
		//文件包位置
		pkg = getInitParameter("packages");
		isdebug = BooleanUtils.toBoolean(getInitParameter("isdebug"));
		Extension = getInitParameter("extension");
		//系统扩展名
		if(StringUtils.isBlank(Extension)) Extension = "wk";
	}

	public void destroy() {
		super.destroy();
	}
	
	
	@Override
	protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		process(RequestContext.get(), false);
	}
	
	@Override
	protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		process(RequestContext.get(), false);
	}
	
	@Override
	protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		process(RequestContext.get(), false);
	}
	
	@Override
	protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		process(RequestContext.get(), false);
	}
	
	@Override
	protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		process(RequestContext.get(), false);
	}
	
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		process(RequestContext.get(), false);
	}

	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		process(RequestContext.get(), true);
	}
	
	/**
	 * 执行Action方法并进行返回处理、异常处理
	 * @param req
	 * @param resp
	 * @param is_post
	 * @throws ServletException
	 * @throws IOException
	 */
	protected void process(RequestContext context, boolean is_post) 
		throws ServletException, IOException 
	{
		try{
			context.response().setContentType("text/html;charset=utf-8");
			if(_process(context, is_post)){ 
				//
			}
		}catch(Exception e){
			log("Exception in action process.", e);
			throw new ServletException(e);
		}finally{
//			g_json_enabled.remove();
		}
	}
	
	/**
	 * 业务逻辑处理
	 * @param req
	 * @param resp
	 * @param is_post_method
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 * @throws IOException 
	 * @throws ServletException
	 * @throws IOException
	 * @throws InvocationTargetException 
	 * @throws IllegalArgumentException 
	 */
	private boolean _process(RequestContext context, boolean is_post)
			 throws InstantiationException,
					IllegalAccessException, 
					IOException, 
					IllegalArgumentException,
					InvocationTargetException, ServletException
	{
//		String theServletPath = request.getServletPath() + request.getPathInfo();
		Long stime = System.currentTimeMillis();
		String requestURI = context.uri();
		requestURI = requestURI.replaceFirst(context.request().getContextPath(), "");
		if(urlpattern.get(requestURI)!=null){
			UrlPattern urlPattern =urlpattern.get(requestURI);
			log.debug(requestURI+"is cache urlPattern.getForward()="+urlPattern.getForward()+"\turlPattern.getDotype="+urlPattern.getDotype());
			if(urlPattern.getDotype()==UrlPattern.DOTYPE_FORWARD){
				context.setAttribute("pageendtime",System.currentTimeMillis());
				context.setAttribute("pagestarttime",stime);
				forward(urlPattern.getForward(),context);
				return true;
			}else{
				Method m_action = urlPattern.getM_action();
				ActionForward fr = null;
				try{
					fr = (ActionForward)m_action.invoke(urlPattern.getAction(), new Object[] {context });
				} catch (Exception e) {
					fr = ExceptionUtil.getActionForward(e, context);
				}
				context.setAttribute("pagestarttime",stime);
				context.setAttribute("pageendtime",System.currentTimeMillis());
				if(fr==null){
					forward(urlPattern.getForward(),context);
					return true;
				}else{
					Map<String, Object> root = new HashMap<String, Object>();
					if(StringUtils.equalsIgnoreCase("tojson", fr.getUri())){
						fr.setUri("allpage/tojson");
						root.put("jsonfile", context.getAttribute("jsonfile"));
						fr.setRoot(root);
						fr.setType(ActionForward.TYPE_VM);
					}else if(StringUtils.equalsIgnoreCase("cg", fr.getUri())){
						fr.setUri("allpage/cg");
						root.put("script", context.getAttribute("script"));
						fr.setRoot(root);
						fr.setType(ActionForward.TYPE_VM);
					}else if(StringUtils.equalsIgnoreCase("sb", fr.getUri())){
						fr.setUri("allpage/sb");
						root.put("script", context.getAttribute("script"));
						fr.setRoot(root);
						fr.setType(ActionForward.TYPE_VM);
					}else if(StringUtils.equalsIgnoreCase("toxml", fr.getUri())){
						fr.setUri("allpage/toxml");
						root.put("xmlfile", context.getAttribute("xmlfile"));
						fr.setRoot(root);
						fr.setType(ActionForward.TYPE_VM);
					}else if(StringUtils.equalsIgnoreCase("tofile", fr.getUri())){
						return true;
					} else if(fr.getUri()==null){
						fr.setUri(urlPattern.getForward());
						return true;
					}else if(fr.getUri()==null){
						fr.setUri(urlPattern.getForward());
					}
					if(fr.getUri().length()>4&&StringUtils.equalsIgnoreCase("."+Extension,StringUtils.right(fr.getUri(), 4))){
						context.redirect(fr.getUri());
					}else{
						forward(fr,context);
					}
					return true;
				}
			}
		}else{
			log.debug(requestURI+"is no cache ");
			UrlPattern urlPattern = new UrlPattern();
			urlPattern.setUrl(requestURI);
			log.debug("requestURI="+requestURI);
			String[] parts = StringUtils.split(requestURI, '/');
			String action_method_name = "execute";
			String claname = "index";
			String pkgname = "";
			int k=parts.length;
			if(k<=1){
				claname = parts[0];
				claname = StringUtils.replace(claname, "."+Extension, "");
			}else{
				for(int i=0;i<(k-2);i++){
					if(i>0) 
						pkgname = pkgname+".";
					pkgname=pkgname+parts[i];
				}
				action_method_name = parts[k-1];
				claname = parts[k-2];
			}
			action_method_name = StringUtils.replace(action_method_name, "."+Extension, "");
			Object action = this._LoadAction(pkgname,claname,context);
			urlPattern.setAction(action);
			//没有Action方法
			if(action==null){
				urlPattern.setDotype(UrlPattern.DOTYPE_FORWARD);
				if(StringUtils.isBlank(pkgname)){
					if(k<=1){
						urlPattern.setForward(claname);
					}else{
						urlPattern.setForward(claname+"/"+action_method_name);
					}
				}else{
					urlPattern.setForward(pkgname.replaceAll("\\.", "/")+"/"+claname+"/"+action_method_name);
				}
				if(!isdebug)urlpattern.put(urlPattern.getUrl(), urlPattern);
				forward(urlPattern.getForward(),context);
				context.setAttribute("pagestarttime",stime);
				context.setAttribute("pageendtime",System.currentTimeMillis());
				return true;
			}else{
				Method m_action = this._GetActionMethod(action, action_method_name);
				urlPattern.setM_action(m_action);
				ActionForward fr = null;
				try{
					fr = (ActionForward)m_action.invoke(action, new Object[] {context });
				} catch (Exception e) {
					fr = ExceptionUtil.getActionForward(e, context);
				}
				log.debug("ActionForward="+fr);
				context.setAttribute("pagestarttime",stime);
				context.setAttribute("pageendtime",System.currentTimeMillis());
				if(fr==null){
					//方法不存在
					urlPattern.setDotype(UrlPattern.DOTYPE_FORWARD);
					if(StringUtils.isBlank(pkgname)){
						if(k<=1){
							urlPattern.setForward(claname);
						}else{
							urlPattern.setForward(claname+"/"+action_method_name);
						}
					}else{
						urlPattern.setForward(pkgname+"/"+claname+"/"+action_method_name);
					}
					if(!isdebug)urlpattern.put(urlPattern.getUrl(), urlPattern);
					forward(urlPattern.getForward(),context);
					return true;
				}else{
					if(!isdebug)urlpattern.put(urlPattern.getUrl(), urlPattern);
					Map<String, Object> root = new HashMap<String, Object>();
					if(StringUtils.equalsIgnoreCase("tojson", fr.getUri())){
						fr.setUri("allpage/tojson");
						root.put("jsonfile", context.getAttribute("jsonfile"));
						fr.setRoot(root);
						fr.setType(ActionForward.TYPE_VM);
					}else if(StringUtils.equalsIgnoreCase("cg", fr.getUri())){
						fr.setUri("allpage/cg");
						root.put("script", context.getAttribute("script"));
						fr.setRoot(root);
						fr.setType(ActionForward.TYPE_VM);
					}else if(StringUtils.equalsIgnoreCase("sb", fr.getUri())){
						fr.setUri("allpage/sb");
						root.put("script", context.getAttribute("script"));
						fr.setRoot(root);
						fr.setType(ActionForward.TYPE_VM);
					}else if(StringUtils.equalsIgnoreCase("toxml", fr.getUri())){
						fr.setUri("allpage/toxml");
						root.put("xmlfile", context.getAttribute("xmlfile"));
						fr.setRoot(root);
						fr.setType(ActionForward.TYPE_VM);
					}else if(StringUtils.equalsIgnoreCase("tofile", fr.getUri())){
						return true;
					} else if(fr.getUri()==null){
						fr.setUri(urlPattern.getForward());
						return true;
					}
					if(fr.getUri().length()>4&&StringUtils.equalsIgnoreCase("."+Extension,StringUtils.right(fr.getUri(), 4))){
						context.redirect(fr.getUri());
					}else{
						forward(fr,context);
					}
					return true;
				}
			}
		}
	}
	
	protected Object _LoadAction(String pkgname,String act_name,RequestContext context) 
			throws InstantiationException,IllegalAccessException 
		{
			Object action = null;
			if(action==null){
				if(StringUtils.isNotBlank(pkgname)) pkgname = "."+pkgname;
				String cls = pkg+pkgname+"." + StringUtils.capitalize(act_name) + "Action";
				action = _LoadActionOfFullname(act_name, cls);
			}
			return action ;
		}
	
	private Object _LoadActionOfFullname(String act_name, String cls) 
			throws IllegalAccessException, InstantiationException 
		{
			Object action = null;
			try {
				log.debug("Classpath:"+cls);
				action = Class.forName(cls).newInstance();
			} catch (ClassNotFoundException excp) {
//				log.error(excp);
				log.debug("找不到类"+excp.getMessage());
			}
			return action;
		}
	
	
	/**
	 * 获取名为{method}的方法
	 * @param action
	 * @param method
	 * @return
	 */
	private Method _GetActionMethod(Object action,String filed) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException{
		Method[] methods = action.getClass().getMethods();
		for (int i = 0; i < methods.length; i++) {
			Method method = methods[i];
			if(StringUtils.equalsIgnoreCase(filed,method.getName())){
				return method;
			}
		}
		return null;
	}
	
	
	////////////////////////////////////////////////跳转页面

	
	/**
	 * 页面跳转
	 * @param fr
	 * @throws ServletException
	 * @throws IOException
	 */
	public void forward(ActionForward fr,RequestContext context) throws ServletException, IOException {
		//Template t = RequestContext.getpageTemplate("/WEB-INF/vm/"+fr.getUri()+".ftl");
		//如果使用VM模板方式
		Map<String, Object> root = fr.getRoot();
		root.put("ContextRoot", context.request().getContextPath());//
		root.put("pageendtime",context.getAttribute("pageendtime"));
		root.put("pagestarttime",context.getAttribute("pagestarttime"));
		fr.setRoot(root);
		if(ActionForward.TYPE_VM.equalsIgnoreCase(fr.getType())){
			Template t = null;
			if(StringUtils.equalsIgnoreCase(ActionForward.TYPE_VM, fr.getType())){
				//使用模板
				log.debug("vm/"+fr.getUri()+".ftl");
				if(!templates.containsKey(fr.getUri())){
					try{
						t = RequestContext.getpageTemplate("vm/"+fr.getUri()+".ftl");
					}catch(IOException e){
						t=null;
						log.debug(e);
						try{
							t = RequestContext.getTemplate("vm/"+fr.getUri()+".ftl");
						}catch(IOException e1){
							log.debug(e);
							t=null;
						}
					}
					if(!isdebug)templates.put(fr.getUri(), t);
				}else{
					log.debug(fr.getUri()+" is cache ,user cache");
					t = templates.get(fr.getUri());
				}
			}
			if(t==null){
				context.response().setStatus(HttpServletResponse.SC_NOT_FOUND);
				context.response().sendError(HttpServletResponse.SC_NOT_FOUND);
			}else{
				try {
					t.process(fr.getRoot(),context.response().getWriter());
					return;
				} catch (TemplateException e) {
					e.printStackTrace();
					context.response().setStatus(HttpServletResponse.SC_NOT_FOUND);
				}
			}
		}else if(ActionForward.TYPE_JSP.equalsIgnoreCase(fr.getType())){
			RequestDispatcher rd = context.context().getRequestDispatcher("/WEB-INF/jsp/"+fr.getUri()+".jsp");
			rd.forward(context.request(), context.response());
		}else{
			if(StringUtils.isNotBlank(fr.getUri())&&!StringUtils.equalsIgnoreCase("/",StringUtils.right(fr.getUri(), 1))){
				fr.setUri("/"+fr.getUri());
			}
			RequestDispatcher rd = null;
			if(fr.getUri().indexOf(".")>=0){
				rd = context.context().getRequestDispatcher(fr.getUri());
			}else{
				rd = context.context().getRequestDispatcher("/WEB-INF/jsp/"+fr.getUri()+".jsp");
			}
			rd.forward(context.request(), context.response());
		}
	}
	
	public void forward(String uri,RequestContext context) throws ServletException, IOException{
		ActionForward forward = new ActionForward(uri);
		if(uri.length()>4&&StringUtils.equalsIgnoreCase(".jsp",StringUtils.right(uri, 4))){
			forward.setType(ActionForward.TYPE_JSP);
		}else{
			Template t = null;
			log.debug("vm/"+uri+".ftl");
			if(!templates.containsKey(uri)){
				try{
					t = RequestContext.getpageTemplate("vm/"+uri+".ftl");
				}catch(IOException e){
					t=null;
					log.debug(e);
					try{
						t = RequestContext.getTemplate("vm/"+uri+".ftl");
					}catch(IOException e1){
						log.debug(e);
						t=null;
					}
				}
				if(!isdebug)templates.put(uri, t);
			}else{
				log.debug(uri+" is cache ,user cache");
				t = templates.get(uri);
			}
			//找不到模板直接使用jsp
			if(t==null){
				forward.setType(ActionForward.TYPE_JSP);
				forward.setUri(uri);
			}else{
				forward.setType(ActionForward.TYPE_VM);
				Map<String, Object> input = new HashMap<String, Object>();
				forward.setRoot(input);
			}
		}
		forward(forward,context);
	}

}